package wsi.exp;

import java.io.File;
import java.util.HashMap;
import java.util.Map;

import org.rygh.core.algorithm.PrecisionRecall;
import org.rygh.core.html.Html;
import org.rygh.core.html.table.HtmlTable;
import org.rygh.core.html.table.TableData;
import org.rygh.core.html.table.TableTuple;
import org.rygh.core.util.UIo;
import org.rygh.core.xml.UXml;
import org.rygh.core.xml.XmlPath;

import wsi.dataguide.DataGuide;
import wsi.dataguide.DataGuideLoader;
import wsi.function.Function;
import wsi.script.AppConfig;

public class Exp1SelectedTree extends Experiment {

	public static XmlPath getFunctionSubrootPath(Function function) {
		Exp1SelectedTree exp1 = new Exp1SelectedTree();
		exp1.load1();

		XmlPath gold = exp1.getGoldSetMap1().get(function);

		return gold;
	}

	public static boolean isFunctionCorrectlyDetected(Function function) {
		if (function == null)
			return false;

		Exp1SelectedTree exp1 = new Exp1SelectedTree();
		exp1.load1();

		XmlPath goldMatch = exp1.getGoldSetMap1().get(function);

		return isFunctionCorrectlyDetected(function, goldMatch);
	}

	public static boolean isFunctionCorrectlyDetected(Function function,
			XmlPath goldMatch) {
		if (function == null)
			return false;

		if (goldMatch == null) {
			System.out.println("[error] exp1 gold set is empty.");
			return false;
		}

		Exp1SelectedTree exp1 = new Exp1SelectedTree();
		exp1.load1();

		Map<Function, XmlPath> autoMap = exp1.getAutomaticMap1();
		XmlPath autoMatch = autoMap.get(function);

		if (autoMatch == null) {
			System.out.printf("[warn] exp1 function %s is not yet computed.\n",
					function.getFullName());
			return false;
		} else if (!autoMatch.stripIndex().equals(goldMatch.stripIndex())) {
			System.out.printf(
					"[warn] exp1 function %s wrong match %s, expect %s.\n",
					function.getFullName(), autoMatch, goldMatch);
			return false;
		}

		return true;
	}

	protected final Map<Function, Boolean> exp1ResultMap = new HashMap<>();
	protected final Map<Function, XmlPath> goldSetMap1 = new HashMap<>();
	private final Map<Function, XmlPath> automaticMap1 = new HashMap<>();
	private PrecisionRecall precisionRecall;

	public Map<Function, XmlPath> getAutomaticMap1() {
		return automaticMap1;
	}

	public Map<Function, XmlPath> getGoldSetMap1() {
		return goldSetMap1;
	}

	public int judge1(Map<Function, XmlPath> autoMap,
			Map<Function, XmlPath> goldMap, Map<Function, Boolean> exp1ResMap) {
		int c = 0;

		for (Function function : autoMap.keySet()) {
			if (!goldMap.containsKey(function)) {
				System.out.printf("[warn] gold set 1 does not contain %s.\n",
						function.getFullName());
				continue;
			}

			XmlPath pg, pa;

			pg = goldMap.get(function);
			pa = autoMap.get(function);

			if (pg.stripIndex().equals(pa.stripIndex())) {
				exp1ResMap.put(function, true);
				c++;
			} else
				exp1ResMap.put(function, false);
		}

		return c;
	}

	private void load1() {
		ExpFolderController folder = ExpFolderController.shared();

		loadXml1(folder.getFolderForExp1(), automaticMap1);

		loadGold1(goldSetMap1);
	}

	public void loadGold1(File dir, Map<Function, XmlPath> gold) {
		loadXml1(dir, gold);
	}

	public void loadGold1(Map<Function, XmlPath> gold) {
		loadGold1(ExpFolderController.shared().getGoldFolderForExp1(), gold);
	}

	public void loadXml1(File folder, Map<Function, XmlPath> map) {
		for (File file : folder.listFiles(UXml.XML_FILTER)) {
			String fname = UIo.getFileNameWithoutExtension(file.getName());
			Function function = parseFileName(fname);

			String content = UIo.readFile(file);
			DataGuideLoader loader = new DataGuideLoader(content);
			DataGuide dg = loader.load();

			map.put(function, dg.getSelectedNode().getXpath());
		}
	}

	@Override
	public void compute() {
		load1();
		//

		int goldSize = goldSetMap1.size();
		int resultSize = automaticMap1.size();
		int correctSize = judge1(automaticMap1, goldSetMap1, exp1ResultMap);

		precisionRecall = new PrecisionRecall(goldSize);
		precisionRecall.setRelevant(correctSize);
		precisionRecall.setRetrieved(resultSize);
	}

	@Override
	public String save() {
		TableData data = new TableData();
		TableTuple tuple = new TableTuple();

		tuple.add("Item", "Value");
		data.add(tuple);

		tuple = new TableTuple();
		tuple.add("Gold Set Size g", precisionRecall.getGoldSize());
		data.add(tuple);

		tuple = new TableTuple();
		tuple.add("Answer Set Size a", precisionRecall.getRetrieved());
		data.add(tuple);

		tuple = new TableTuple();
		tuple.add("correct(a)", precisionRecall.getRelevant());
		data.add(tuple);

		tuple = new TableTuple();
		tuple.add(
				"Precision: correct(a)/a",
				String.format("%.2f%%",
						precisionRecall.getPrecisionInPercentage()));
		data.add(tuple);

		tuple = new TableTuple();
		tuple.add("Recall: correct(a)/g", String.format("%.2f%%",
				precisionRecall.getRecallInPercentage()));
		data.add(tuple);

		//

		Html html = new Html("Experiment 1");
		html.addElement(AppConfig.shared().getConfigHtml());

		HtmlTable table = new HtmlTable("Experiment 1, Selected Tree", data);
		html.addElement(table);
		html.newline();

		boolean contains;

		data = new TableData();
		tuple = new TableTuple();

		tuple.add("Service", "Function", "Gold Set", "Automatic");
		data.add(tuple);

		//

		for (Function function : automaticMap1.keySet()) {
			contains = goldSetMap1.containsKey(function);

			tuple = new TableTuple();

			tuple.add(function.getService());
			tuple.add(function.getName());

			XmlPath g = goldSetMap1.get(function);
			tuple.add(contains ? g.stripIndex() : "");

			XmlPath a = automaticMap1.get(function);
			tuple.add(a.stripIndex());

			if (!contains)
				tuple.setBackgroundColor(Color.blue);
			else if (!g.stripIndex().equals(a.stripIndex()))
				tuple.setBackgroundColor(Color.red);

			data.add(tuple);
		}

		data.sort();

		appendMissingHtml(html, goldSetMap1.keySet(), automaticMap1.keySet());

		//

		table = new HtmlTable("Experiment 1, Detailed Information", data);
		html.addElement(table);
		html.newline();

		//

		String htmlstr = html.toString();

		ExpFolderController folder = ExpFolderController.shared();
		UIo.saveFile(new File(folder.getResultFolder(), ExpFolderController.EXP
				+ "1_result.html"), htmlstr);

		return htmlstr;
	}
}
